
#include <BFont>
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BSystem>

#include "member_BComboBox.h"
#include "member_BComboItem.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BComboBox*)_ptr)
#define member_allocate()		_ptr = new member_BComboBox(this)
#define member_release()		delete (member_BComboBox*)_ptr

BComboBox::BComboBox(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BComboBox::BComboBox(Form form, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.alignment.ia_form = form;
}
BComboBox::BComboBox(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.block.text = text;
}
BComboBox::~BComboBox()
{
	member_release();
}

void BComboBox::setAlign(Align align)
{
	if (member.alignment.ia_align != align)
	{
		member.alignment.ia_align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BComboBox::align() const
{
	return member.alignment.ia_align;
}

void BComboBox::setForm(Form form)
{
	if (member.alignment.ia_form != form)
	{
		member.alignment.ia_form = form;
		member.dirty = true;
		for (int i = 0; i < member.items.size(); i++)
		{
			BComboItem* item = member.items[i];
			item_member(item)->dirty = true;
		}
		member.board->dirty = true;
		member.board->fresh();
		this->fresh();
	}
}
Form BComboBox::form() const
{
	return member.alignment.ia_form;
}

void BComboBox::setSpacing(int spacing)
{
	if (member.alignment.ia_spacing != spacing)
	{
		member.alignment.ia_spacing = spacing;
		member.dirty = true;
		this->fresh();
	}
}
int BComboBox::spacing() const
{
	return member.alignment.ia_spacing;
}

void BComboBox::setEditable(bool editable)
{
	if (member.editable != editable)
	{
		member.editable = editable;
		if (editable)
			this->setCursor(Cursor_Ibeam);
		else
			this->setCursor(Cursor_None);
		for (int i = 0; i < member.items.size(); i++)
		{
			BComboItem* item = member.items[i];
			item_member(item)->dirty = true;
		}
		member.dirty = true;
		this->emit(Signal_Editable, editable);
		this->fresh();
	}
}
bool BComboBox::editable() const
{
	return member.editable;
}

void BComboBox::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		member.dirty = true;
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
	}
}
bool BComboBox::readOnly() const
{
	return member.readOnly;
}

void BComboBox::setArrowVisible(bool arrowVisible)
{
	if (member.arrow->setVisible(arrowVisible))
	{
		member.dirty = true;
		this->fresh();
	}
}
bool BComboBox::arrowVisible() const
{
	return member.arrow;
}

void BComboBox::setArrowSize(const BSize& arrowSize)
{
	if (member.arrowSize != arrowSize)
	{
		member.arrowSize = arrowSize;
		this->fresh();
	}
}
const BSize& BComboBox::arrowSize() const
{
	return member.arrowSize;
}

void BComboBox::setIconSize(const BSize& iconSize)
{
	if (member.iconSize != iconSize)
	{
		member.iconSize = iconSize;
	}
}
const BSize& BComboBox::iconSize() const
{
	return member.iconSize;
}

bool BComboBox::setText(const BString& text)
{
	BComboItem* item = itemOfText(text);
	if (!item && member.editable)
	{
		this->setCurrent(0);
		int end = text.find('\n');
		member.block.text.reset(text, 0, end);
		member.block.reset();
		member.dirty = true;
		this->fresh();
		return true;
	}
	return this->setCurrent(item);
}
const BString& BComboBox::text() const
{
	if (member.current)
		return member.current->text();
	return member.block.text;
}

void BComboBox::setTextFormat(BTextFormat* textFormat)
{
	if (member.block.format != textFormat)
	{
		member.block.format = textFormat;
		if (textFormat)
		{
			textFormat->styled(this->realStyle());
			textFormat->changed(member.block.text);
		}
		member.dirty = true;
		this->emit(Signal_FormatChanged, textFormat);
		this->fresh();
	}
}
const BTextFormat* BComboBox::textFormat() const
{
	return member.block.format;
}
BTextFormat* BComboBox::textFormat()
{
	return member.block.format;
}

void BComboBox::setValue(const BValue& value)
{
	if (member.editable && member.current == 0)
	{
		if (member.value != value)
		{
			member.value = value;
			emit(Signal_ValueChanged, member.value);
		}
		return;
	}
	if (member.value != value)
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			BComboItem* item = member.items[i];
			if (item->value() == value)
			{
				setCurrent(item);
			}
		}
	}
}
const BValue& BComboBox::value() const
{
	return member.value;
}

bool BComboBox::addItem(BComboItem* item)
{
	if (!item)
		return false;
	if (item_member(item)->combox == this)
		return true;
	if (!member.items.append(item))
		return false;
	if (item_member(item)->combox)
	{
		item_member(item)->combox->removeItem(item);
	}
	item_member(item)->combox = this;
	member.connect(item, Signal_ColorChanged, &member_BComboBox::slotItemColorChanged);
	member.connect(item, Signal_GraphChanged, &member_BComboBox::slotItemGraphChanged);
	member.connect(item, Signal_ImageChanged, &member_BComboBox::slotItemImageChanged);
	member.connect(item, Signal_IconChanged, &member_BComboBox::slotItemIconChanged);
	member.connect(item, Signal_TextChanged, &member_BComboBox::slotItemTextChanged);
	member.connect(item, Signal_ValueChanged, &member_BComboBox::slotItemValueChanged);
	member.board->dirty = true;
	member.board->fresh();
	member.dirty = true;
	this->emit(Signal_ItemInserted, item);
	this->adjust();
	return true;
}
BComboItem* BComboBox::addItem(const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(text, value);
	addItem(item);
	return item;
}
BComboItem* BComboBox::addItem(Graph graph, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(graph, text, value);
	addItem(item);
	return item;
}
BComboItem* BComboBox::addItem(BIcon* icon, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(icon, text, value);
	addItem(item);
	return item;
}
BComboItem* BComboBox::addItem(BImage* image, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(image, text, value);
	addItem(item);
	return item;
}

bool BComboBox::insertItem(int pos, BComboItem* item)
{
	if (!item)
		return false;
	if (item_member(item)->combox == this)
	{
		int index = member.board->items->find(item);
		member.board->items->move(index, pos);
		return true;
	}
	if (!member.items.insert(pos, item))
		return false;
	if (item_member(item)->combox)
	{
		item_member(item)->combox->removeItem(item);
	}
	item_member(item)->combox = this;
	member.connect(item, Signal_ColorChanged, &member_BComboBox::slotItemColorChanged);
	member.connect(item, Signal_GraphChanged, &member_BComboBox::slotItemGraphChanged);
	member.connect(item, Signal_ImageChanged, &member_BComboBox::slotItemImageChanged);
	member.connect(item, Signal_IconChanged, &member_BComboBox::slotItemIconChanged);
	member.connect(item, Signal_TextChanged, &member_BComboBox::slotItemTextChanged);
	member.connect(item, Signal_ValueChanged, &member_BComboBox::slotItemValueChanged);
	member.board->dirty = true;
	member.board->fresh();
	member.dirty = true;
	this->emit(Signal_ItemInserted, item);
	this->fresh();
	return true;
}
BComboItem* BComboBox::insertItem(int pos, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(text, value);
	insertItem(pos, item);
	return item;
}
BComboItem* BComboBox::insertItem(int pos, Graph graph, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(graph, text, value);
	insertItem(pos, item);
	return item;
}
BComboItem* BComboBox::insertItem(int pos, BIcon* icon, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(icon, text, value);
	insertItem(pos, item);
	return item;
}
BComboItem* BComboBox::insertItem(int pos, BImage* image, const BString& text, const BValue& value)
{
	BComboItem* item = new BComboItem(image, text, value);
	insertItem(pos, item);
	return item;
}

bool BComboBox::removeItem(BComboItem* item)
{
	if (!item || item_member(item)->combox != this)
		return false;
	if (member.current == item)
	{
		setCurrent(0);
	}
	item_member(item)->combox = 0;
	BHolder<BComboItem> holder(item);
	member.items.remove(item);
	member.disconnect(item);
	member.board->hoveredItem = 0;
	member.board->pressedItem = 0;
	member.board->dirty = true;
	member.board->fresh();
	this->emit(Signal_ItemRemoved, item);
	return true;
}
bool BComboBox::removeItem(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->text() == text)
		{
			return removeItem(item);
		}
	}
	return false;
}
bool BComboBox::removeItem(int index)
{
	BComboItem* item = member.items(index);
	return removeItem(item);
}
void BComboBox::clearItems()
{
	if (member.items.size())
	{
		while (BComboItem* item = member.items.last())
		{
			BHolder<BComboItem> holder(item);
			member.items.pop();
			member.disconnect(item);
			this->emit(Signal_ItemRemoved, item);
		}
		member.items.clear();
		member.board->dirty = true;
		member.board->fresh();
		member.dirty = true;
		member.block.text.clear();
		if (member.current)
			setCurrent(0);
		this->fresh();
	}
}

bool BComboBox::containItem(const BComboItem* item) const
{
	return member.items.contain(item);
}
bool BComboBox::containItem(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->value() == value)
			return true;
	}
	return false;
}
bool BComboBox::containItem(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->text() == text)
			return true;
	}
	return false;
}

int BComboBox::itemIndex(const BComboItem* item) const
{
	return member.items.find(item);
}
int BComboBox::itemIndex(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->value() == value)
			return i;
	}
	return -1;
}
int BComboBox::itemIndex(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->text() == text)
			return i;
	}
	return -1;
}

int BComboBox::itemCount() const
{
	return member.items.size();
}
BComboItem* BComboBox::item(int index)
{
	return member.items(index);
}
const BComboItem* BComboBox::item(int index) const
{
	return member.items(index);
}

BComboItem* BComboBox::itemOfText(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->text() == text)
			return item;
	}
	return 0;
}
const BComboItem* BComboBox::itemOfText(const BString& text) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BComboItem* item = member.items[i];
		if (item->text() == text)
			return item;
	}
	return 0;
}

BComboItem* BComboBox::itemOfValue(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->value() == value)
			return item;
	}
	return 0;
}
const BComboItem* BComboBox::itemOfValue(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BComboItem* item = member.items[i];
		if (item->value() == value)
			return item;
	}
	return 0;
}

bool BComboBox::moveItem(const BComboItem* item, int pos)
{
	int index = member.items.find(item);
	if (member.items.move(index, pos))
	{
		int min = bMin(index, pos);
		int max = bMax(index, pos);
		member.board->fresh();
		return true;
	}
	return false;
}
bool BComboBox::moveItem(const BString& text, int pos)
{
	BComboItem* item = itemOfText(text);
	return moveItem(item, pos);
}
bool BComboBox::moveItem(const BValue& value, int pos)
{
	BComboItem* item = itemOfValue(value);
	return moveItem(item, pos);
}

bool BComboBox::setCurrent(BComboItem* item)
{
	if (item && item_member(item)->combox != this)
		return false;
	if (member.current != item)
	{
		member.current = item;
		member.block.clear();
		member.value.clear();
		if (member.current)
		{
			member.value = item->value();
		}
		member.board->dirty = true;
		member.board->fresh();
		member.dirty = true;
		if (this->checks(Signal_TextChanged))
			emit(Signal_TextChanged, this->text());
		if (this->checks(Signal_ValueChanged))
			emit(Signal_ValueChanged, member.value);
		if (this->checks(Signal_CurrentChanged))
			emit(Signal_CurrentChanged, member.current);
		this->fresh();
		return true;
	}
	return false;
}
const BComboItem* BComboBox::current() const
{
	return member.current;
}
BComboItem* BComboBox::current()
{
	return member.current;
}

bool BComboBox::setCurrentText(const BString& text)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		if (item->text() == text)
		{
			return setCurrent(item);
		}
	}
	return setCurrent(0);
}
const BString& BComboBox::currentText() const
{
	if (member.current)
		return member.current->text();
	return member.block.text;
}

bool BComboBox::setCurrentIndex(int index)
{
	BComboItem* item = member.items(index);
	return setCurrent(item);
}
int BComboBox::currentIndex() const
{
	if (member.current)
		return member.current->index();
	return -1;
}

void BComboBox::setMaxShowCount(int maxShowCount)
{
	if (member.board->maxShowCount != maxShowCount)
	{
		member.board->maxShowCount = maxShowCount;
		member.board->dirty = true;
		member.board->fresh();
	}
}
int BComboBox::maxShowCount() const
{
	return member.board->maxShowCount;
}

void BComboBox::setPopupOnClick(bool popupOnClick)
{
	if (member.popupOnClick != popupOnClick)
	{
		member.popupOnClick = popupOnClick;
	}
}
bool BComboBox::popupOnClick() const
{
	return member.popupOnClick;
}

void BComboBox::showBoard()
{
	member.arrow->setChecked(true);
}
void BComboBox::hideBoard()
{
	member.arrow->setChecked(false);
}

BSize BComboBox::sizeHint() const
{
	if (member.board->dirty)
	{
		member.board->freshItemRects();
		member.board->dirty = false;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize baseSize = member.board->maxItemSize;
	if (member.editable)
	{
		const BFont* font = this->realFont();
		baseSize.width() = bMax(baseSize.width(), font->size() * 10);
		baseSize.height() = bMax(baseSize.height(), font->size());
	}
	return this->preferSize(baseSize);
}

bool BComboBox::query(const BString& text, BValue& value) const
{
	if (text == "board")
	{
		value = member.board;
		return true;
	}
	if (text == "arrow")
	{
		value = member.arrow;
		return true;
	}
	if (text == "icon-rect")
	{
		value = member.alignment.ia_iconRect;
		return true;
	}
	if (text == "text-rect")
	{
		value = member.block.rect;
		return true;
	}
	return BWidget::query(text, value);
}

void BComboBox::updateEvent(const BEvent& event)
{
	if (this->focused() && member.editable)
	{
		bool blinkShown = true;
		if (member.blinkTime > 0.0)
		{
			BReal now = event.now();
			BReal time = now - member.blinkTick;
			blinkShown = time <= (member.blinkTime / 2);
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.blinkShown != blinkShown)
		{
			member.blinkShown = blinkShown;
			this->fresh();
		}
	}
}
void BComboBox::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BComboBox::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = event.value();

	style_member(style)->assign(Value_Form, member.alignment.ia_form);
	style_member(style)->assign(Value_Align, member.alignment.ia_align);
	style_member(style)->assign(Value_Spacing, member.alignment.ia_spacing);

	if (BTextFormat* format = member.block.format)
	{
		format->styled(style);
	}
	if (const BFont* font = this->realFont())
	{
		member.arrow->setIconSize(font->size() / 2);
	}

	for (int i = 0; i < member.items.size(); i++)
	{
		BComboItem* item = member.items[i];
		item_member(item)->dirtyStyle = true;
	}
	member.board->dirty = true;

	member.dirty = true;
}
void BComboBox::freshEvent(const BEvent& event)
{
	if (member.board->dirty)
	{
		member.board->freshItemRects();
		member.board->dirty = false;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BComboBox::paintEvent(const BEvent& event)
{
	BPainter painter(this);

	State state = painter.state();
	if (this->hovered())
	{
		if (member.pressed)
			state |= State_Pressed;
		else
			state |= State_Hovered;
	}

	painter.drawBackground(state);
	painter.drawForeground(state);
	painter.drawBorder(state);
	painter.drawPerch(state);
	painter.drawMargin(state);

	if (this->focused() && member.block.count > 0)
	{
		const BRect& crect = this->centerRect();
		int inputCount = member.block.count;
		painter.setColor(Color_Selection, state);
		for (int i = 0; i < member.block.count; i++)
		{
			int index = member.block.index + i;
			const ICodeCell& cell = member.block.cells[index];
			BRect rect = cell.rect + member.block.rect.pos();
			if (rect.right() < crect.left())
				continue;
			if (rect.left() > crect.right())
				break;
			painter.fillRect(rect);
		}
	}

	const BRect& crect = this->centerRect();
	painter.setClip(crect);

	if (member.block.rect.valid())
	{
		painter.setColor(Color_Text, state);
		member.block.paint(painter);
	}

	if (member.current && member.alignment.ia_iconRect.valid())
	{
		BImage* image = member.current->image();
		if (BIcon* icon = member.current->icon())
		{
			image = icon->image();
		}
		if (image)
		{
			painter.setColor(member.current->color());
			painter.drawImage(member.alignment.ia_iconRect, image, true);
		}
		else if (member.current->graph())
		{
			painter.setColor(member.current->color());
			painter.setLineSmooth(true);
			painter.drawGraph(member.alignment.ia_iconRect % 80, member.current->graph());
		}
	}

	if (member.editable && member.blinkShown)
	{
		painter.setColor(Color_Indicator);
		BLine2 line = member.inputLine();
		painter.drawLine(line.begin(), line.end());
	}
}

void BComboBox::activeEvent(const BEvent& event)
{
	if (event.value() == false)
	{
		member.arrow->setChecked(false);
	}
	BWidget::activeEvent(event);
}

void BComboBox::focusEvent(const BEvent& event)
{
	if (member.editable)
	{
		if (this->focused())
		{
			member.focusedTime = event.time();
			member.block.index = 0;
			member.block.count = member.block.cells.size();
			member.blinkShown = true;
			this->fresh();
		}
		else
		{
			if (member.editing)
			{
				member.editing = false;
				if (member.current)
					member.current->setText(member.block.text);
				emit(Signal_EditFinished, member.block.text);
			}
			member.blinkShown = false;
			this->fresh();
		}
	}
	BWidget::focusEvent(event);
}
void BComboBox::inputEvent(const BEvent& event)
{
	if (member.editable)
	{
		BCode code = event.value();
		bool edited = member.block.removeSelection();
		if (member.block.insert(code))
		{
			edited = true;
		}
		if (edited)
		{
			member.emitEdited();
			BLine2 line = member.inputLine();
			event.accept(line.end());
			this->fresh();
		}
	}
	BWidget::inputEvent(event);
}

void BComboBox::keyPressEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	if (keyEvent.keyState(Key_Ctrl))
	{
		if (key == Key_C)
		{
			BString text;
			if (member.block.index > -1)
			{
				if (member.block.count > 0)
					member.block.collect(text, member.block.index, member.block.count);
				else
					text = member.block.text;
			}
			BSystem::SetClippedText(text);
			keyEvent.accept(true);
		}
		else if (keyEvent.key() == Key_A)
		{
			member.block.index = 0;
			member.block.count = member.block.cells.size();
			member.blinkShown = true;
			this->fresh();
			keyEvent.accept(true);
		}
		else if (member.editable)
		{
			if (key == Key_V)
			{
				if (member.block.index > -1 && member.block.count > 0)
				{
					member.block.remove(member.block.index, member.block.count);
				}
				BString text;
				bool ret = BSystem::GetClippedText(text);
				text.replace('\n', ' ');
				member.block.insert(text);
				member.emitEdited();
				keyEvent.accept(true);
			}
			else if (key == Key_X)
			{
				BString text;
				if (member.block.index >= 0)
				{
					if (member.block.count)
						member.block.collect(text, member.block.index, member.block.count);
					else
						text = member.block.text;
				}
				BSystem::SetClippedText(text);
				if (member.block.removeSelection())
					member.emitEdited();
				keyEvent.accept(true);
			}
		}
	}
	if (member.editable)
	{
		if (key == Key_Enter)
		{
			keyEvent.accept(true);
			if (member.editing)
			{
				member.editing = false;
				member.dirty = true;
				if (member.current)
					member.current->setText(member.block.text);
				emit(Signal_EditFinished, member.block.text);
			}
			member.blinkShown = false;
			this->fresh();
		}
		else if (key == Key_Left)
		{
			if (member.block.count > 0)
			{
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index > 0)
			{
				member.block.index--;
				this->fresh();
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Right)
		{
			if (member.block.count > 0)
			{
				member.block.index += member.block.count;
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index < member.block.cells.size())
			{
				member.block.index++;
				this->fresh();
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Back)
		{
			if (member.block.count > 0)
			{
				if (member.block.remove(member.block.index, member.block.count))
				{
					member.block.count = 0;
					member.emitEdited();
					this->fresh();
				}
			}
			else
			{
				member.block.index--;
				if (member.block.remove(member.block.index))
				{
					member.emitEdited();
					this->fresh();
				}
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept(true);
		}
		else if (key == Key_Delete)
		{
			if (member.block.count > 0)
			{
				if (member.block.remove(member.block.index, member.block.count))
				{
					member.block.count = 0;
					member.emitEdited();
					this->fresh();
				}
			}
			else
			{
				if (member.block.remove(member.block.index))
				{
					member.emitEdited();
					this->fresh();
				}
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept(true);
		}
	}
	BWidget::keyPressEvent(keyEvent);
}
void BComboBox::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	BWidget::keyReleaseEvent(keyEvent);
}

void BComboBox::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.editable)
	{
		member.hoverIndex = -1;
		this->setCursor(Cursor_Arrow);
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BComboBox::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.editable)
	{
		member.hoverIndex = -1;
		this->setCursor(Cursor_Arrow);
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BComboBox::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.time() - member.focusedTime < (BReal)0.3)
	{
		return;
	}
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		int hoverIndex = member.hoverIndex;
		if (member.block.index != member.hoverIndex)
		{
			member.block.index = member.hoverIndex;
			member.block.count = 0;
			this->fresh();
		}
		member.pressed = true;
		member.pressIndex = member.hoverIndex;
		member.blinkTick = mouseEvent.time() + (BReal)0.5;
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BComboBox::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = false;
		member.pressIndex = -1;
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BComboBox::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.editable)
	{
		if (mouseEvent.button() == Button_None && this->focusable())
		{
			if (this->centerRect().contain(pos))
				this->setCursor(Cursor_Ibeam);
			else
				this->setCursor(Cursor_Arrow);
		}
		int index = member.block.indexAt(pos);
		if (member.hoverIndex != index)
		{
			member.hoverIndex = index;
		}
		if (member.pressed)
		{
			int inputIndex = bMin(member.pressIndex, member.hoverIndex);
			int inputCount = abs(member.pressIndex - member.hoverIndex);
			if (member.block.index != inputIndex || member.block.count != inputCount)
			{
				member.block.index = inputIndex;
				member.block.count = inputCount;
				this->fresh();
			}
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BComboBox::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	const BRect& crect = this->centerRect();
	if (crect.contain(pos) && !member.editable && !member.readOnly)
	{
		if (member.popupOnClick && mouseEvent.button() == Button_Left)
		{
			bool checked = member.arrow->checked();
			member.arrow->setChecked(!checked);
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
void BComboBox::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	const BRect& crect = this->centerRect();
	if (mouseEvent.button() == Button_Left)
	{
		if (crect.contain(pos) && !member.editable)
		{
			if (member.block.selectWord())
			{
				this->fresh();
			}
		}
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}

